/* 
 * File:   smpi.c
 * Author: Vagner Clementino
 *
 * Created on 8 de Abril de 2012, 11:34
 */

#include <stdio.h>
#include <stdlib.h>
#include "smpi.h"


void smpi(Lista_Aluno alunos, Lista_Universidade universidades, int heuristica,
          FILE* arquivo_saida){
    
    Grafo* casamentos = NULL;
    casamentos = cria_grafo(alunos->num_itens, universidades->num_itens);
    inicializa_grafo(&casamentos);
    
    tabela_vagas* vagas = NULL;
    vagas = cria_tabela_vagas(casamentos->num_universidades,2);
    define_tabela_vagas(&vagas,universidades);
    
    cria_grafo_casamentos(&casamentos,alunos,&vagas,heuristica);
    
    matricula_alunos(casamentos,&universidades);
    
    imprime_resultados(universidades,alunos->num_itens,heuristica,arquivo_saida);   
}

void cria_grafo_casamentos(Grafo** grafo_casamento, Lista_Aluno alunos,
                           tabela_vagas** vagas, int heuristica){
    
    int alunos_matriculados = 0;
    int alunos_lista_esgotada = 0;
    int total_alunos = (*grafo_casamento)->num_alunos;
    int i;
    int universidade_pretendida;
    int candidato_troca;
    Aluno* a;
    
    switch (heuristica){
        case 1:/*Maximiza satisfacao dos alunos*/
            while ( (alunos_matriculados + alunos_lista_esgotada) != total_alunos){
                for(i=0;i<total_alunos;i++){
                    if(esta_matriculado(i,*grafo_casamento) == FALSO){
                        a = busca_aluno(i+1,alunos);
                        universidade_pretendida = retorna_universidade(a);
                        if(universidade_pretendida == -1){
                            alunos_lista_esgotada++;
                        }else{
                            if(tem_vaga(universidade_pretendida-1,*vagas) 
                               == VERDADEIRO){
                               (*grafo_casamento)->dados[i][universidade_pretendida-1].tem_aresta = VERDADEIRO;
                               (*grafo_casamento)->dados[i][universidade_pretendida-1].nota_aluno = a->nota;
                               (*grafo_casamento)->dados[i][universidade_pretendida-1].satisfacao = a->ultima_universidade;
                               retira_vaga(universidade_pretendida-1,vagas,1);
                               alunos_matriculados++;
                            }else{
                                candidato_troca = retorna_candidato_troca(universidade_pretendida,
                                                                         *grafo_casamento,heuristica);
                                if( a->ultima_universidade < (*grafo_casamento)->dados[candidato_troca][universidade_pretendida-1].satisfacao){
                                    (*grafo_casamento)->dados[i][universidade_pretendida-1].tem_aresta = VERDADEIRO;
                                    (*grafo_casamento)->dados[i][universidade_pretendida-1].nota_aluno = a->nota;
                                    (*grafo_casamento)->dados[i][universidade_pretendida-1].satisfacao = a->ultima_universidade;
                                    (*grafo_casamento)->dados[candidato_troca][universidade_pretendida-1].tem_aresta = FALSO;                                    
                                }
                                
                            }
                        }
                        
                    }
                    
                }
            }
            break;
            
        case 2:
            while ( (alunos_matriculados + alunos_lista_esgotada) != total_alunos){
                for(i=0;i<total_alunos;i++){
                    if(esta_matriculado(i,*grafo_casamento) == FALSO){
                        a = busca_aluno(i+1,alunos);
                        universidade_pretendida = retorna_universidade(a);
                        if(universidade_pretendida == -1){
                            alunos_lista_esgotada++;
                        }else{
                            if(tem_vaga(universidade_pretendida-1,*vagas) 
                               == VERDADEIRO){
                               (*grafo_casamento)->dados[i][universidade_pretendida-1].tem_aresta = VERDADEIRO;
                               (*grafo_casamento)->dados[i][universidade_pretendida-1].nota_aluno = a->nota;
                               (*grafo_casamento)->dados[i][universidade_pretendida-1].satisfacao = a->ultima_universidade;
                               retira_vaga(universidade_pretendida-1,vagas,1);
                               alunos_matriculados++;
                            }else{
                                candidato_troca = retorna_candidato_troca(universidade_pretendida,
                                                                         *grafo_casamento,heuristica);
                                if( a->nota > (*grafo_casamento)->dados[candidato_troca][universidade_pretendida-1].nota_aluno){
                                    (*grafo_casamento)->dados[i][universidade_pretendida-1].tem_aresta = VERDADEIRO;
                                    (*grafo_casamento)->dados[i][universidade_pretendida-1].nota_aluno = a->nota;
                                    (*grafo_casamento)->dados[i][universidade_pretendida-1].satisfacao = a->ultima_universidade;
                                    (*grafo_casamento)->dados[candidato_troca][universidade_pretendida-1].tem_aresta = FALSO;                                    
                                }
                                
                            }
                        }
                        
                    }
                    
                }
            }
            break;
        default:
            printf("Heuristica nao conhecida\n");           
    }
}

void matricula_alunos(const Grafo* grafo_casamento, Lista_Universidade* universidades){
    Nodo_Universidade* aux;
    int i;
    int index_lista = 0;
    
    if(esta_vazia_lista_universidade(*universidades)){
        printf("Lista vazia.\n");
        return;
    }else{
        aux = (*universidades)->primeiro;
        while(aux != NULL){
            for(i=0;i<grafo_casamento->num_alunos;i++){
                if(grafo_casamento->dados[i][(aux->u.id)-1].tem_aresta == VERDADEIRO){
                    aux->u.lista_alunos[index_lista].id_aluno = i+1;
                    aux->u.lista_alunos[index_lista].nota_aluno = grafo_casamento->dados[i][(aux->u.id)-1].nota_aluno;
                    aux->u.lista_alunos[index_lista].grau_satisfacao = grafo_casamento->dados[i][(aux->u.id)-1].satisfacao;
                    aux->u.vagas_preenchidas++;
                    index_lista++;
                    ordena_alunos_matriculados( &(aux->u.lista_alunos),aux->u.vagas_preenchidas);
                    
                }
                
            }
            
            aux = (Nodo_Universidade*) aux->next;
        }
        
        
    }
}


float calcula_media(int* vetor_valores, int tam_vetor){
    int i;
    float acumulador = 0.0;
    
    for(i=0;i<tam_vetor;i++){
        acumulador += vetor_valores[i];
    }
    return(acumulador/tam_vetor);
}

float calcula_mediana(int* vetor_valores, int tam_vetor){
    int mediana = 0;
    int i,j;
    
    if(tam_vetor % 2 ==1 ){
        i = (tam_vetor)/2;
        mediana = (float) vetor_valores[i];        
    }else{
        i = (tam_vetor-1) / 2;
        j = (tam_vetor) / 2;
        mediana = (float) ( (vetor_valores[i]+vetor_valores[j]) / 2);        
    }    
    return (mediana);
}

float calcula_vagas_preenchidas(const Lista_Universidade universidades,
                                int tipo_metrica){
    float metrica = 0.0;
    Nodo_Universidade* aux;
    int* vetor_valores = NULL;
    int i = 0;
    
    
    vetor_valores = (int*) malloc(universidades->num_itens * sizeof(int));
    
   
    if(esta_vazia_lista_universidade(universidades)){
        printf("Lista vazia.\n");
        return metrica;
        }else{
            aux = universidades->primeiro;
            while(aux != NULL){
                vetor_valores[i] = (aux->u.vagas_preenchidas / aux->u.num_vagas);
                i++;
            aux = (Nodo_Universidade*) aux->next;
            }
        }
    imprime_vetor_metricas(vetor_valores,universidades->num_itens);
    QuickSort(vetor_valores,universidades->num_itens);
    imprime_vetor_metricas(vetor_valores,universidades->num_itens);

    switch (tipo_metrica){
        case 1: /*Media*/
            metrica = calcula_media(vetor_valores, i);
            break;
        case 2: /*Mediana*/
            metrica = calcula_mediana(vetor_valores,i);
            break;
        default:
            printf("Metrica desconhecida\n");
    }
    
    return metrica;
}

float calcula_satisfacao_alunos(const Lista_Universidade universidades,
                                int num_alunos, int tipo_metrica){
    float metrica = 0.0;
    Nodo_Universidade* aux;
    int* vetor_valores = NULL;
    int i = 0;
    int j;
    
    
    vetor_valores = (int*) malloc(num_alunos * sizeof(int));
    
   
    if(esta_vazia_lista_universidade(universidades)){
        printf("Lista vazia.\n");
        return;
        }else{
            aux = (universidades)->primeiro;
            while(aux != NULL){
                for(j=0;j<aux->u.vagas_preenchidas;j++){
                   vetor_valores[i] = (aux->u.lista_alunos[i].grau_satisfacao);
                   i++;
                }                
            aux = (Nodo_Universidade*) aux->next;
            }
        }
    //Ordena vetor
    imprime_vetor_metricas(vetor_valores,num_alunos);
    QuickSort(vetor_valores,universidades->num_itens);
    imprime_vetor_metricas(vetor_valores,num_alunos);
    
    switch (tipo_metrica){
        case 1: /*Media*/
            metrica = calcula_media(vetor_valores, i);
            break;
        case 2: /*Mediana*/
            metrica = calcula_mediana(vetor_valores,i);
            break;
        default:
            printf("Metrica desconhecida\n");
    }
    
    return metrica;
}

float calcula_satisfacao_univer(const Lista_Universidade universidades,
                                int num_alunos, int tipo_metrica){
    
    float metrica = 0.0;
    Nodo_Universidade* aux;
    int* vetor_valores = NULL;
    int i = 0;
    int j;
    
    
    vetor_valores = (int*) malloc(num_alunos * sizeof(int));
    
   
    if(esta_vazia_lista_universidade(universidades)){
        printf("Lista vazia.\n");
        return;
        }else{
            aux = (universidades)->primeiro;
            while(aux != NULL){
                for(j=0;j<aux->u.vagas_preenchidas;j++){
                   vetor_valores[i] = (aux->u.lista_alunos[i].nota_aluno);
                   i++;
                }                
            aux = (Nodo_Universidade*) aux->next;
            }
        }
    //Ordena vetor
    imprime_vetor_metricas(vetor_valores,num_alunos);
    QuickSort(vetor_valores,universidades->num_itens);
    imprime_vetor_metricas(vetor_valores,num_alunos);
    
    switch (tipo_metrica){
        case 1: /*Media*/
            metrica = calcula_media(vetor_valores, i);
            break;
        case 2: /*Mediana*/
            metrica = calcula_mediana(vetor_valores,i);
            break;
        default:
            printf("Metrica desconhecida\n");
    }
    
    return metrica;
}

float calcula_nota_corte(const Lista_Universidade universidades,
                         int tipo_metrica){
    float metrica = 0.0;
    Nodo_Universidade* aux;
    int* vetor_valores = NULL;
    int i = 0;
    
    
    vetor_valores = (int*) malloc(universidades->num_itens * sizeof(int));
    
   
    if(esta_vazia_lista_universidade(universidades)){
        printf("Lista vazia.\n");
        return;
        }else{
            aux = (universidades)->primeiro;
            while(aux != NULL){
                vetor_valores[i] = (aux->u.lista_alunos[(aux->u.vagas_preenchidas)-1].nota_aluno);
                i++;
            aux = (Nodo_Universidade*) aux->next;
            }
        }
    //Ordena vetor
    imprime_vetor_metricas(vetor_valores,universidades->num_itens);
    QuickSort(vetor_valores,universidades->num_itens);
    imprime_vetor_metricas(vetor_valores,universidades->num_itens);
    
    switch (tipo_metrica){
        case 1: /*Media*/
            metrica = calcula_media(vetor_valores, i);
            break;
        case 2: /*Mediana*/
            metrica = calcula_mediana(vetor_valores,i);
            break;
        default:
            printf("Metrica desconhecida\n");
    }    
    return metrica;
}

void imprime_vetor_metricas(int* v, int tam_vetor){
    int i;
    for(i=0;i<tam_vetor;i++){
        printf("%d ", v[i]);
    }
    printf("\n");
}

void imprime_aprovados(const Lista_Universidade universidades, int heuristica,
                       FILE* arquivo_saida){
    
    Nodo_Universidade* aux;
    
    if(esta_vazia_lista_universidade(universidades)){
        printf("Lista vazia.\n");
        return;
        }else{
        printf("#Heuristica%d",heuristica);
            aux = (universidades)->primeiro;
            while(aux != NULL){
                imprime_alunos_matriculados(&(aux->u),aux->u.vagas_preenchidas,
                                            arquivo_saida);
                aux = (Nodo_Universidade*) aux->next;
            }
        }
}

void imprime_metricas(const Lista_Universidade universidades, int num_alunos,
                      FILE* arquivo_saida){
    
    float media_vagas_preench, mediana_vagas_preench;
    float media_satisf_aluno, mediana_satisf_aluno;
    float media_satisf_univ, mediana_satisf_univ;
    float media_nota_corte, mediana_nota_corte;
    
    media_vagas_preench = calcula_vagas_preenchidas(universidades,1);
    mediana_vagas_preench = calcula_vagas_preenchidas(universidades,2);
    
    media_satisf_aluno = calcula_satisfacao_alunos(universidades,num_alunos,1);
    mediana_satisf_aluno = calcula_satisfacao_alunos(universidades,num_alunos,2);
    
    media_satisf_univ= calcula_satisfacao_univer(universidades,num_alunos,1);
    mediana_satisf_univ = calcula_satisfacao_univer(universidades,num_alunos,2);
    
    media_nota_corte = calcula_nota_corte(universidades, 1);
    mediana_nota_corte = calcula_nota_corte(universidades, 2);
    
    fprintf(arquivo_saida,"%f.2 %f.2\n",media_vagas_preench, mediana_vagas_preench);
    
    fprintf(arquivo_saida,"%f.2 %f.2\n",media_satisf_aluno,mediana_satisf_aluno);
            
    fprintf(arquivo_saida,"%f.2 %f.2\n", media_satisf_univ,media_satisf_univ);
            
    fprintf(arquivo_saida,"%f.2 %f.2\n", media_nota_corte,mediana_nota_corte);  
    
}

void imprime_resultados(const Lista_Universidade universidades,int num_alunos, 
                        int heuristica, FILE* arquivo_saida){
    
    imprime_aprovados(universidades,heuristica,arquivo_saida);
    imprime_metricas (universidades,num_alunos,arquivo_saida);
    
}
                                                              

